mod e_paper;
use crate::e_paper::{SpiDriverPin, HEIGHT, WIDTH};
use canvas_sys::{
    Canvas, CanvasOpt, DotPixel, DotStyle, DrawFill, DrawStyle, Font, LineStyle, Rotate,
};
use esp_idf_svc::hal::adc::*;
use esp_idf_svc::hal::gpio::{
    Gpio10, Gpio11, Gpio15, Gpio18, Gpio19, Gpio20, Input, Output, PinDriver,
};
use esp_idf_svc::hal::peripherals::Peripherals;
use esp_idf_svc::hal::prelude::*;

use esp_idf_svc::hal::adc::attenuation::DB_11;
use esp_idf_svc::hal::adc::oneshot::config::AdcChannelConfig;
use esp_idf_svc::hal::adc::oneshot::*;
use esp_idf_svc::hal::adc::oneshot::{AdcChannelDriver, AdcDriver};
use esp_idf_svc::log::EspLogger;
use esp_idf_svc::sys::{esp_get_free_heap_size, vTaskList};
use std::thread::sleep;
use std::time::Duration;

const WHITE: u8 = 0xff;
const BLACK: u8 = 0x0;

fn main() -> anyhow::Result<()> {
    esp_idf_svc::sys::link_patches();
    EspLogger::initialize_default();

    manual()
}

pub struct SpiGpioDrivePin<'d> {
    busy: PinDriver<'d, Gpio20, Input>,
    rst: PinDriver<'d, Gpio19, Output>,
    dc: PinDriver<'d, Gpio18, Output>,
    sclk: PinDriver<'d, Gpio11, Output>,
    sdo: PinDriver<'d, Gpio10, Output>,
    cs: PinDriver<'d, Gpio15, Output>,
}
impl<'d> SpiDriverPin for SpiGpioDrivePin<'d> {
    fn set_cs(&mut self, is_high: bool) -> anyhow::Result<()> {
        if is_high {
            self.cs.set_high()?
        } else {
            self.cs.set_low()?
        }
        Ok(())
    }

    fn set_dc(&mut self, is_high: bool) -> anyhow::Result<()> {
        if is_high {
            self.dc.set_high()?
        } else {
            self.dc.set_low()?
        }
        Ok(())
    }

    fn set_sdo(&mut self, is_high: bool) -> anyhow::Result<()> {
        if is_high {
            self.sdo.set_high()?
        } else {
            self.sdo.set_low()?
        }
        Ok(())
    }

    fn set_rst(&mut self, is_high: bool) -> anyhow::Result<()> {
        if is_high {
            self.rst.set_high()?
        } else {
            self.rst.set_low()?
        }
        Ok(())
    }

    fn set_sck(&mut self, is_high: bool) -> anyhow::Result<()> {
        if is_high {
            self.sclk.set_high()?
        } else {
            self.sclk.set_low()?
        }
        Ok(())
    }

    fn busy(&self) -> bool {
        self.busy.is_high()
    }
}

fn manual() -> anyhow::Result<()> {
    let peripherals = Peripherals::take()?;

    let adc = AdcDriver::new(peripherals.adc1)?;

    // configuring pin to analog read, you can regulate the adc input voltage range depending on your need
    // for this example we use the attenuation of 11db which sets the input voltage range to around 0-3.6V
    let config = AdcChannelConfig {
        attenuation: DB_11,
        calibration: true,
        ..Default::default()
    };

    let mut adc_pin = AdcChannelDriver::new(&adc, peripherals.pins.gpio2, &config)?;

    let busy = PinDriver::input(peripherals.pins.gpio20)?;
    let rst = PinDriver::output(peripherals.pins.gpio19)?;
    let dc = PinDriver::output(peripherals.pins.gpio18)?;
    let sclk = PinDriver::output(peripherals.pins.gpio11)?;
    // let sdi = peripherals.pins.gpio9;
    let sdo = PinDriver::output(peripherals.pins.gpio10)?;
    let cs = PinDriver::output(peripherals.pins.gpio15)?;

    let spi_pin = SpiGpioDrivePin {
        busy,
        rst,
        dc,
        sclk,
        sdo,
        cs,
    };
    let mut ctx = e_paper::v3::SpiContextV3::new(spi_pin);

    println!("init display");
    ctx.init(e_paper::EPD_2IN13_FULL)?;
    println!("clear display");
    ctx.clear()?;

    println!("start loop display");
    let mut canvas = Canvas::new(CanvasOpt {
        width: WIDTH,
        height: HEIGHT,
        rotate: Rotate::R90,
    });
    let mut r_canvas = Canvas::new(CanvasOpt {
        width: WIDTH,
        height: HEIGHT,
        rotate: Rotate::R90,
    });
    let mut count = 0;
    loop {
        println!("clear screen");
        canvas.clear(WHITE.into());
        r_canvas.clear(WHITE.into());

        println!("draw line");
        canvas.active();
        canvas.draw_line(
            5,
            5,
            WIDTH - 5,
            HEIGHT - 5,
            BLACK.into(),
            DotPixel::DotPixel1x1,
            LineStyle::LineStyleSolid,
        );
        let mut buf = [0u8; 2048];
        unsafe {
            vTaskList(buf.as_mut_ptr() as *mut core::ffi::c_char);
        }
        canvas.draw_cstring(
            0,
            20,
            buf.as_slice(),
            Font::BM12,
            BLACK.into(),
            WHITE.into(),
        );

        // println!("draw circle");
        // canvas.draw_circle(
        //     30,
        //     90,
        //     10,
        //     BLACK.into(),
        //     DotPixel::DotPixel2x2,
        //     DrawStyle::DrawFillFull,
        // );
        // println!("draw number");
        // canvas.draw_number(75, 75, 1000, Font::BM12, BLACK.into(), WHITE.into());
        println!("draw text");
        let s = format!("Refresh num: {:05}\0", count);
        canvas.draw_cstring(0, 0, s.as_bytes(), Font::BM12, BLACK.into(), WHITE.into());

        r_canvas.active();
        let m = format!("Mem Free: {} KB\0", unsafe {
            esp_get_free_heap_size() / 1024
        });
        r_canvas.draw_cstring(0, 10, m.as_bytes(), Font::BM12, BLACK.into(), WHITE.into());

        let adc_val = adc.read(&mut adc_pin)?;
        println!("ADC value: {}", adc_val);
        let adc_s = format!("adc: {}\0", adc_val);
        r_canvas.draw_cstring(
            0,
            70,
            adc_s.as_bytes(),
            Font::BM12,
            BLACK.into(),
            WHITE.into(),
        );

        // println!("draw rect");
        // r_canvas.draw_rect(
        //     100,
        //     80,
        //     130,
        //     100,
        //     BLACK.into(),
        //     DotPixel::DotPixel2x2,
        //     DrawFill::DrawFillFull,
        // );
        _ = ctx.display(canvas.data(), r_canvas.data());

        sleep(Duration::from_secs(5));
        _ = ctx.sleep();
        sleep(Duration::from_secs(180));
        count += 1;
    }
    Ok(())
}
